//*************************************************************************************************
// TFSSSApiShim.cpp
//*************************************************************************************************
#include "stdafx.h"
#include <initguid.h>						// Cause GUIDs to be instantiated.
#include <objbase.h>
#include "olehelp.h"
#include "ssautoi.h"
#include "resource.h"
#include "TFSComApi.h"

// Typeinfo holder classes.
GUID		CTypeInfo::m_TLBID = LIBID_SourceSafeTypeLib;
LPCWSTR		CTypeInfo::m_szTLB = L"ssapi.dll";
int			CTypeInfo::m_iTypeLibMajorVersion = SOURCESAFE_TYPELIB_VERSION_MAJOR;
int			CTypeInfo::m_iTypeLibMinorVersion = SOURCESAFE_TYPELIB_VERSION_MINOR;
ITypeLib	*CTypeInfo::m_pITypeLib = NULL;
LCID		CTypeInfo::m_lcid;
CTypeInfo	CImpIVSSItem::m_TI(IID_IVSSItem);
CTypeInfo	CVSSVersions::m_TI(IID_IVSSVersions);
CTypeInfo	CVSSVersion::m_TI(IID_IVSSVersion);
CTypeInfo	CVSSItems::m_TI(IID_IVSSItems);
CTypeInfo	CVSSChkouts::m_TI(IID_IVSSCheckouts);
CTypeInfo	CVSSChkout::m_TI(IID_IVSSCheckout);
CTypeInfo	CImpIVSSDatabase::m_TI(IID_IVSSDatabase);
CTypeInfo	CVSSUser::m_TI(IID_IVSSUser);
CTypeInfo	CVSSUsers::m_TI(IID_IVSSUsers);

CCritLock	gsMutex;
bool		gfImpersonate = false;

extern HINSTANCE ghInst;

#ifdef _DEBUG
LONG giVSSItem = 0;

// A static class to make sure all objects are gone on shutdown.
class CAllObjectsGone
{
public:
	~CAllObjectsGone()
	{
		assert(giVSSItem == 0);
		assert(giObj == 0);
	}
} AllObjectsGone;
#endif


IMPLEMENT_AUTOMATION(CImpIVSSItem)

//*************************************************************************************************
// Get the SourceSafe spec for this item.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_Spec(			    // Return status.
	BSTR		*pSpec)					    // Buffer for the spec.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_Spec(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

    if (pSpec == NULL)
	{
		return HRError(E_INVALIDARG);
	}

	// Convert to a BSTR.
    *pSpec = SysAllocString(m_pObj->m_sItem.ItemPath);
	if (*pSpec == NULL)
    {
		return HRError(E_OUTOFMEMORY);
    }
	return S_OK;
}

//*************************************************************************************************
// Determine if the specified item is a binary file.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_Binary(		    // Return status.
	VARIANT_BOOL *pbBinary)				    // Buffer for the state.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_Binary(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

	if (pbBinary == NULL)
	{
		return HRError(E_INVALIDARG);
	}

	// Make sure the item is a file.
    if (m_pObj->m_sItem.ItemType != TFSItemType_File)
	{
		return SSError(ERS_VS_WANT_FILE);
	}

    if (m_pObj->m_sItem.Encoding == -1)
	{
		*pbBinary = VARIANT_TRUE;
	}
	else
	{
		*pbBinary = VARIANT_FALSE;
	}
	return S_OK;
}

//*************************************************************************************************
// Set the specified file as binary (or text).
//*************************************************************************************************
HRESULT CImpIVSSItem::put_Binary(		    // Return status.
	VARIANT_BOOL bBinary)				    // The new state.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::put_Binary(this = \"%ls\", bBinary = %d)", m_pObj->m_sItem.ItemPath, bBinary);

    //TODO:
    return HRError(E_NOTIMPL);
}

//*****************************************************************************
// Get encoding of this item.
//*****************************************************************************
HRESULT CImpIVSSItem::get_Encoding(			// Return status.
	VSSItemEncoding* pItemEncoding)		    // Returned encoding
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_Encoding(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

    if (pItemEncoding == NULL)
	{
		return HRError(E_INVALIDARG);
	}

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Set encoding for this item.
//*************************************************************************************************
HRESULT CImpIVSSItem::put_Encoding(		    // Return status.
	VSSItemEncoding ItemEncoding)		    // Buffer for the name.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::put_Encoding(this = \"%ls\", ItemEncoding = %d)", m_pObj->m_sItem.ItemPath, ItemEncoding);

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Determine if encoding type auto-detection is enabled for the file.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_AutoDetectEncoding(	// Return status.
	VARIANT_BOOL *pbAutoDetectEncoding)			// Buffer for the state.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_AutoDetectEncoding(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Enable/Disable encoding type auto-detection for the file.
//*************************************************************************************************
HRESULT CImpIVSSItem::put_AutoDetectEncoding(// Return status.
	VARIANT_BOOL bAutoDetectEncoding)		// The new state.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::put_AutoDetectEncoding(this = \"%ls\", bAutoDetectEncoding = %d)", m_pObj->m_sItem.ItemPath, bAutoDetectEncoding);

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Get the SourceSafe physical filename for this item.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_Physical(		    // Return status.
	BSTR		*pPhysical)					// Buffer for the spec.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_Physical(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

	if (!pPhysical)
	{
		return HRError(E_POINTER);
	}

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Get the SourceSafe CRC checksum for this item.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_CRC(		        // Return status.
	long		*pCRC)				        // Buffer for the crc.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_CRC(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

	if (!pCRC)
	{
		return HRError(E_POINTER);
	}
	
    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Determine if the specified item is deleted.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_Deleted(		    // Return status.
	VARIANT_BOOL *pbDeleted)			    // Buffer for the state.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_Deleted(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

	if (!pbDeleted)
	{
		return HRError(E_POINTER);
	}

	if (m_pObj->m_sItem.DeletionId != 0)
    {
		*pbDeleted = VARIANT_TRUE;
    }
	else
    {
		*pbDeleted = VARIANT_FALSE;
    }
	return S_OK;
}

//*************************************************************************************************
// Delete or recover the item.
//*************************************************************************************************
HRESULT CImpIVSSItem::put_Deleted(		    // Return status.
	VARIANT_BOOL bDeleted)				    // Buffer for the state.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::put_Deleted(this = \"%ls\", bDeleted = %d)", m_pObj->m_sItem.ItemPath, bDeleted);

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Determine if the specified item file or a project.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_Type(			    // Return status.
	int			*piType)				    // Buffer for the type.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_Type(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

	if (!piType)
	{
		return HRError(E_POINTER);
	}

    if (m_pObj->m_sItem.ItemType == TFSItemType_Folder)
    {
		*piType = VSSITEM_PROJECT;
    }
	else
    {
		*piType = VSSITEM_FILE;
    }
	return S_OK;
}

//*************************************************************************************************
// Get the Local spec for this item.  For a project this is the same as the working directory.  If
// a working directory is set, return S_OK.  If not use the current directory and return S_FALSE.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_LocalSpec(	    // Return status.
	BSTR		*pLocal)				    // Buffer for the spec.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_LocalSpec(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

	if (!pLocal)
	{
		return HRError(E_POINTER);
	}

    // Get the working folder for this item in the FPSE workspace.
    HRESULT hr;
    ITFSWorkspace *pIWorkspace;
    TFSWorkingFolder sWorkingFolder;
    if (FAILED(hr = m_pObj->m_pVSSDatabase->GetFPSEWorkspace(&pIWorkspace)) ||
        FAILED(hr = pIWorkspace->GetWorkingFolderForServerItem(m_pObj->m_sItem.ItemPath, &sWorkingFolder)))
    {
        return HRError(hr);
    }

    // Return the local path if it is mapped and "" if not.
    if (sWorkingFolder.Type == TFSWorkingFolderType_Map)
    {
        *pLocal = sWorkingFolder.LocalPath;
        *pLocal = NULL;
    }
    else
    {
        *pLocal = SysAllocString(L"");
    }
    sWorkingFolder.Delete();
    return S_OK;
}

//*************************************************************************************************
// Set the Local spec for this item.  For a project this is the same as the working directory.
//*************************************************************************************************
HRESULT CImpIVSSItem::put_LocalSpec(	    // Return status.
	BSTR		Local)					    // Buffer for the spec.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::put_LocalSpec(this = \"%ls\", Local = \"%ls\")", m_pObj->m_sItem.ItemPath, Local);

	if (!Local)
	{
		return HRError(E_POINTER);
	}

    HRESULT hr;
    ITFSWorkspace *pIWorkspace;
    if (FAILED(hr = m_pObj->m_pVSSDatabase->GetFPSEWorkspace(&pIWorkspace)) ||
        FAILED(hr = pIWorkspace->SetWorkingFolder(m_pObj->m_sItem.ItemPath, Local)))
    {
        return HRError(hr);
    }
    return S_OK;
}

//*************************************************************************************************
// Get the name for this item.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_Name(			    // Return status.
	BSTR		*pName)					    // Buffer for the name.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_Name(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

	if (!pName)
	{
		return HRError(E_POINTER);
	}

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Set the name for this item.  This is a SourceSafe Rename.
//*************************************************************************************************
HRESULT CImpIVSSItem::put_Name(			    // Return status.
	BSTR		Name)					    // Buffer for the name.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::put_Name(this = \"%ls\", Name = \"%ls\")", m_pObj->m_sItem.ItemPath, Name);

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Get the parent VSSItem for this item.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_Parent(		    // Return status.
	IVSSItem	**ppIParent)			    // Buffer for the parent item.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_Parent(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

	if (!ppIParent)
	{
		return HRError(E_POINTER);
	}

    // Get the info about the folder.
    TFSItem sItem;
    HRESULT hr = m_pObj->m_pVSSDatabase->TFSServer()->GetParentItem(m_pObj->m_sItem.ItemPath, &sItem);
    if (FAILED(hr))
    {
        return HRError(hr);
    }

    // Wrap it in a VSSItem object.
    if (FAILED(hr = CVSSItem::NewVSSItem(sItem, m_pObj->m_pVSSDatabase, ppIParent)))
    {
        sItem.Delete();
        return HRError(hr);
    }

    return S_OK;
}

//*************************************************************************************************
// Get the version # associated with this item.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_VersionNumber(    // Return status.
	long		*piVersion)				    // Buffer for the version #.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_VersionNumber(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

	if (!piVersion)
	{
		return HRError(E_POINTER);
	}

    *piVersion = m_pObj->m_sItem.ChangesetId;
	return S_OK;
}

//*************************************************************************************************
// Get a collection of items in this project.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_Items(		    // Return status.
	VARIANT_BOOL IncludeDeleted,		    // Include deleted children?
	IVSSItems	**ppIItems)				    // Buffer for the collection.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_Items(this = \"%ls\", IncludeDeleted = %d)", m_pObj->m_sItem.ItemPath, IncludeDeleted);

	if (!ppIItems)
	{
		return HRError(E_POINTER);
	}

	// Make sure the item is a project.
    if (m_pObj->m_sItem.ItemType != TFSItemType_Folder)
    {
		return SSError(ERS_VS_WANT_PRJ);
    }

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Get a copy of the item (file or project) to the local directory.
//*************************************************************************************************
HRESULT CImpIVSSItem::Get(				    // Return status.
	BSTR		*Local,					    // Local spec to get to.
	long		iFlags)					    // Flags controlling the operation.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::Get(this = \"%ls\", Local = \"%ls\", iFlags = %d)", m_pObj->m_sItem.ItemPath, UnNULLStr(*Local), iFlags);

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Checkout a copy of the item (file or project) to the local directory.
//*************************************************************************************************
HRESULT CImpIVSSItem::Checkout(			    // Return status.
	BSTR		Comment,				    // A comment.
	BSTR		Local,					    // Local spec to get to.
	long		iFlags)					    // Flags controlling the operation.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::Checkout(this = \"%ls\", Comment = \"%ls\", Local = \"%ls\", iFlags = %d)", m_pObj->m_sItem.ItemPath, UnNULLStr(Comment), UnNULLStr(Local), iFlags);


    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Checkin an item (file or project) from the local directory.
//*************************************************************************************************
HRESULT CImpIVSSItem::Checkin(			    // Return status.
	BSTR		Comment,				    // A comment.
	BSTR		Local,					    // Local spec to checkin from.
	long		iFlags)					    // Flags controlling the operation.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::Checkin(this = \"%ls\", Comment = \"%ls\", Local = \"%ls\", iFlags = %d)", m_pObj->m_sItem.ItemPath, UnNULLStr(Comment), UnNULLStr(Local), iFlags);

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Undo the checkout of an item (file or project).
//*************************************************************************************************
HRESULT CImpIVSSItem::UndoCheckout(		    // Return status.
	BSTR		Local,					    // Local spec of checked out item.
	long		iFlags)					    // Flags controlling the operation.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::UndoCheckout(this = \"%ls\", Local = \"%ls\", iFlags = %d)", m_pObj->m_sItem.ItemPath, UnNULLStr(Local), iFlags);

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Determine if the file is checked out (VSSFILE_CHECKEDOUT, VSSFILE_CHECKEDOUT_ME).
//*************************************************************************************************
HRESULT CImpIVSSItem::get_IsCheckedOut(	    // Return status.
	long		*piStatus)				    // Buffer for the status.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_IsCheckedOut(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

	if (!piStatus)
	{
		return HRError(E_POINTER);
	}

    // Get the pending changes from the server.
    ITFSPendingChanges *pIPendingChanges;
    HRESULT hr = m_pObj->m_pVSSDatabase->TFSServer()->GetPendingChanges(m_pObj->m_sItem.ItemPath, &pIPendingChanges);
    if (FAILED(hr))
    {
        return HRError(hr);
    }

    // Determine how many pending changes there are.
    int count;
    if (FAILED(hr = pIPendingChanges->get_Count(&count)))
    {
        pIPendingChanges->Release();
        return HRError(hr);
    }

    if (count == 0)
    {
        *piStatus = VSSFILE_NOTCHECKEDOUT;
    }
    else
    {
        *piStatus = VSSFILE_CHECKEDOUT;
        for (int i = 0; i < count; ++i)
        {
            // Get the next pending change
            TFSPendingChange pc;
            if (FAILED(hr = pIPendingChanges->GetChange(i, &pc)))
            {
                pIPendingChanges->Release();
                return HRError(hr);
            }

            int cmp = _wcsicmp(pc.WorkspaceOwnerName, m_pObj->m_pVSSDatabase->AuthenticatedUser());
            pc.Delete();
            if (cmp == 0)
            {
                *piStatus = VSSFILE_CHECKEDOUT_ME;
                break;
            }
        }
    }
    pIPendingChanges->Release();
    return S_OK;
}

//*************************************************************************************************
// Get a "collection" of the checkouts for item.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_Checkouts(	    // Return status.
	IVSSCheckouts **ppIChkouts)			    // Checkouts collection.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_Checkouts(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

	if (!ppIChkouts)
	{
		return HRError(E_POINTER);
	}

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Determine if this item is different from the specified local file.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_IsDifferent(	    // Return status.
	BSTR		Local,					    // Local file to compare to.
	VARIANT_BOOL *pbDifferent)		        // Buffer for the status.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_IsDifferent(this = \"%ls\", Local = \"%ls\")", m_pObj->m_sItem.ItemPath, UnNULLStr(Local));

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Add the specified file or directory content to the project and use the given encoding for the
// file(s) instead of auto-detecting it.
//*************************************************************************************************
HRESULT CImpIVSSItem::AddWithEncoding(	    // Return status.
	BSTR			Local,				    // Local spec to get to.
	VSSItemEncoding ItemEncoding,		    // Encoding, when a file is being added
	BSTR			Comment,			    // A comment.
	long			iFlags,				    // Flags controlling the operation.
	IVSSItem		**ppIItem)			    // Newly added item.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::AddWithEncoding(this = \"%ls\", ...)", m_pObj->m_sItem.ItemPath);

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Add the specified file or directory to the project.
//*************************************************************************************************
HRESULT CImpIVSSItem::Add(				    // Return status.
	BSTR		Local,					    // Local spec to get to.
	BSTR		Comment,				    // A comment.
	long		iFlags,					    // Flags controlling the operation.
	IVSSItem	**ppIItem)				    // Newly added item.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::Add(this = \"%ls\", Local = \"%ls\", Comment = \"%ls\", iFlags = %d)", m_pObj->m_sItem.ItemPath, UnNULLStr(Local), UnNULLStr(Comment), iFlags);

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Create a new sub project with the specified name under this project.
//*************************************************************************************************
HRESULT CImpIVSSItem::NewSubproject(	    // Return status.
	BSTR		Name,					    // Name of the subproject.
	BSTR		Comment,				    // A comment.
	IVSSItem	**ppIItem)				    // Newly added item.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::NewSubproject(this = \"%ls\", Name = \"%ls\", Comment = \"%ls\")", m_pObj->m_sItem.ItemPath, UnNULLStr(Name), UnNULLStr(Comment));

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Share the specified item into this project.
//*************************************************************************************************
HRESULT CImpIVSSItem::Share(			    // Return status.
	IVSSItem	*pIItem,				    // Item to share.
	BSTR		Comment,				    // A comment.
	long		iFlags)					    // Flags controlling the operation.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::Share(this = \"%ls\", ...)", m_pObj->m_sItem.ItemPath);

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Permanent delete of the specified item.
//*************************************************************************************************
HRESULT CImpIVSSItem::Destroy()			    // Return status.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::Destroy(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Move this item into the specified project.
//*************************************************************************************************
HRESULT CImpIVSSItem::Move(				    // Return status.
	IVSSItem	*pINewParent)			    // Parent project to move into.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::Move(this = \"%ls\", ...)", m_pObj->m_sItem.ItemPath);

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Label the specified item.
//*************************************************************************************************
HRESULT CImpIVSSItem::Label(			    // Return status.
	BSTR		Label,					    // Label to apply.
	BSTR		Comment)				    // A comment.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::Label(this = \"%ls\", Label = \"%ls\", Comment = \"%ls\")", m_pObj->m_sItem.ItemPath, Label, UnNULLStr(Comment));

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Get a "collection" of the versions in the history of this item.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_Versions(		    // Return status.
	long		iFlags,					    // History options.
	IVSSVersions **ppIVersions)			    // Versions collection.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_Versions(this = \"%ls\", iFlags = %d)", m_pObj->m_sItem.ItemPath, iFlags);

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Get a version of this item.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_Version(		    // Return status.
	VARIANT		Version,				    // Version to get.
	IVSSItem	**ppIItem)				    // Returned item.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_Version(this = \"%ls\", ...)", m_pObj->m_sItem.ItemPath);

	if (ppIItem == NULL)
    {
		return HRError(E_POINTER);
    }

    //TODO:
    return HRError(E_NOTIMPL);
}


//*************************************************************************************************
// Return a collection of all of the VSSItems that are "parents" of this item.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_Links(		    // Return status.
	IVSSItems	**ppIItems)				    // Returned item.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_Links(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

    //TODO:
    return HRError(E_NOTIMPL);
}


//*************************************************************************************************
// Branch the item.
//*************************************************************************************************
HRESULT CImpIVSSItem::Branch(			    // Return status.
	BSTR		Comment,				    // A comment.
	long		iFlags,					    // Flags controlling the operation.
	IVSSItem	**ppIItem)				    // Returned item.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::Branch(this = \"%ls\", Comment = \"%ls\", iFlags = %d)", m_pObj->m_sItem.ItemPath, UnNULLStr(Comment), iFlags);

    //TODO:
    return HRError(E_NOTIMPL);
}


//*************************************************************************************************
// Get the child VSSItem for the supplied relative SourceSafe spec. This property is only available
// for projects.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_Child(	        // Return status.
	BSTR Spec, 						        // The SourceSafe spec.
	VARIANT_BOOL Deleted, 			        // Looking for deleted or not?
	IVSSItem **ppIVSSItem)			        // The retrieved item.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_Child(this = \"%ls\", Spec = \"%ls\", Deleted = %d)", m_pObj->m_sItem.ItemPath, UnNULLStr(Spec), Deleted);

	// Make sure the item is a project.
    if (m_pObj->m_sItem.ItemType != TFSItemType_Folder)
    {
		return SSError(ERS_VS_WANT_PRJ);
    }

    //TODO:
    return HRError(E_NOTIMPL);
}


//*************************************************************************************************
// Get the VSSVersion for this item.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_VSSVersion(       // Return status.
	IVSSVersion **ppIVersion)		        // Buffer for the V
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_VSSVersion(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

    //TODO:
    return HRError(E_NOTIMPL);
}


//*************************************************************************************************
// Get the if this item is currently pinned.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_IsPinned(         // Return status.
	VARIANT_BOOL 	*pbIsPinned)			// Buffer for the version #.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_IsPinned(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

    if (pbIsPinned == NULL)
    {
		return HRError(E_POINTER);
    }

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Determine size of file or a project.
//*************************************************************************************************
HRESULT CImpIVSSItem::get_Size(             // Return status.
	LONG			*piSize)				// Buffer for the type.
{
	INITPUBLICMETHOD(&IID_IVSSItem);
	VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSItem::get_Size(this = \"%ls\")", m_pObj->m_sItem.ItemPath);

    if (piSize == NULL)
    {
		return HRError(E_POINTER);
    }

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
//*************************************************************************************************
#pragma warning(disable : 4355)				// Warning for "this" in initializer.
CVSSItem::CVSSItem(
    TFSItem      &sItem,					// Item this represents.
    CVSSDatabase *pVSSDatabase) :			// Database this item is in.
    m_cRef(0),
    m_ISupErrInfo(this),
    m_IVSSItem(this),
    m_sItem(sItem),
    m_pVSSDatabase(pVSSDatabase)
#pragma warning(default : 4355)
{
    InterlockedIncrement(&giObj);
    DEBUG_STMT(++giVSSItem);
    pVSSDatabase->AddRef();
}

//*************************************************************************************************
//*************************************************************************************************
CVSSItem::~CVSSItem()
{
    InterlockedDecrement(&giObj);
    DEBUG_STMT(--giVSSItem);
    m_sItem.Delete();
    m_pVSSDatabase->Release();
}

//*************************************************************************************************
// Check if we support the requested interface and set the interface pointer appropriately.
//*************************************************************************************************
HRESULT CVSSItem::QueryInterface(		    // Did we get an interface?
    REFIID		riid,					    // ID of the interface we want.
    LPVOID		*ppvObj)				    // Buffer for the interface pointer.
{
    Trace("Enter CVSSItem::QueryInterface(this = \"%ls\")");

    // Init the returned interface pointer to NULL.
    *ppvObj = NULL;

    // If we support the requested interface pointer, save it.
    if (riid == IID_IUnknown)
    {
        *ppvObj = this;
    }
    else if (riid == IID_IDispatch      || 
             riid == IID_IVSSItem       ||
             riid == IID_IVSSItemOld    ||
             riid == IID_IVSSItemOld1   )
    {
        *ppvObj = &m_IVSSItem;
    }
    else if (riid == IID_ISupportErrorInfo)
    {
        *ppvObj = &m_ISupErrInfo;
    }
    else
    {
        return E_NOINTERFACE;
    }

    // AddRef the interface before we return it.
    ((LPUNKNOWN) *ppvObj)->AddRef();
    return S_OK;
}

//*************************************************************************************************
// This is a pretty small wrapper to create a new VSSItem object.  Note this assumes the rights
// checking has already been done.
//*************************************************************************************************
HRESULT CVSSItem::NewVSSItem(			    // Return status.
    TFSItem		&sItem,					    // Item to new.
    CVSSDatabase *pVSSDatabase,			    // Database item is in.
    IVSSItem	**ppIVSSItem)			    // Buffer for the item ptr.
{
    VERIFY_IMPERSONATION
    CVSSItem	*pVSSItem;				    // Used to create new VSSItem.

    // Create a VSSITEM object.
    if ((pVSSItem = new CVSSItem(sItem, pVSSDatabase)) == NULL)
    {
        return E_OUTOFMEMORY;
    }

    // Get the IVSSItem interface.
    pVSSItem->QueryInterface(IID_IVSSItem, (PVOID *) ppIVSSItem);
    return S_OK;
}

IMPLEMENT_AUTOMATION(CImpIVSSDatabase)

//*************************************************************************************************
// Open and login to the specified SourceSafe installation/database.
//*************************************************************************************************
HRESULT CImpIVSSDatabase::Open(			// Return status.
    BSTR		SrcSafeIni,				// SrcSafe.ini to read.
    BSTR		Username,				// User to login as.
    BSTR		Password)				// Password for login.
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);
    VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSDatabase::Open(SrcSafeIni = \"%ls\", Username = \"%ls\")", UnNULLStr(SrcSafeIni), UnNULLStr(Username));

    // Check if the database is already open.
    if (m_pObj->m_bLoggedIn)
    {
        return SSError(ERS_SSAUTO_ALREADYOPEN);
    }

    BSTR ConnectionString;
    if (SrcSafeIni != NULL && wcsstr(SrcSafeIni, L"TFS:") == SrcSafeIni)
    {
        ConnectionString = SysAllocString(SrcSafeIni + 4);
    }
    else
    {
        // If the SrcSafe.ini file was not specified, determine it.
        WCHAR rcPath[MAX_PATH];
        WCHAR rcVol[_MAX_DRIVE], rcDir[_MAX_DIR], rcFilename[_MAX_FNAME];
        if (SrcSafeIni == NULL)
        {
            if (GetModuleFileName(ghInst, rcPath, MAX_PATH) == 0)
            {
                return HRError(__HRESULT_FROM_WIN32(GetLastError()));
            }
            
            _wsplitpath_s(rcPath, rcVol, sizeof(rcVol)/sizeof(WCHAR), rcDir, sizeof(rcDir)/sizeof(WCHAR), rcFilename, sizeof(rcFilename)/sizeof(WCHAR), NULL, 0);
        }
        else
        {
            _wsplitpath_s(SrcSafeIni, rcVol, sizeof(rcVol)/sizeof(WCHAR), rcDir, sizeof(rcDir)/sizeof(WCHAR), rcFilename, sizeof(rcFilename)/sizeof(WCHAR), NULL, 0);
        }
        _wmakepath_s(rcPath, sizeof(rcPath)/sizeof(WCHAR), rcVol, rcDir, L"tfs.ini", NULL);

        HANDLE fh = CreateFile(rcPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (fh == INVALID_HANDLE_VALUE)
        {
            //TODO: Proper error.
            return SSError(-1);
        }
        char rcBuf[MAX_PATH];
        DWORD len;
        ReadFile(fh, rcBuf, sizeof(rcBuf)-1, &len, NULL);
        CloseHandle(fh);
        DWORD i;
        for (i = 0; i < len; ++i)
        {
            if (rcBuf[i] == '\r' || rcBuf[i] == '\n')
            {
                break;
            }
        }
        rcBuf[i] = '\0';
        MultiByteToWideChar(CP_ACP, 0, rcBuf, -1, rcPath, MAX_PATH);
        ConnectionString = SysAllocString(rcPath);
    }
    Trace((LPWSTR)ConnectionString);

    // Copy the string (skipping the TFS:);
    if (ConnectionString == NULL)
    {
        SysFreeString(ConnectionString);
        return SSError(ERS_MEM_NOMEMORY);
    }

    // Create the managed TFS object.
    ITFSServer *pITfs;
    HRESULT hr = CoCreateInstance(CLSID_TFSServer, NULL, CLSCTX_INPROC_SERVER, IID_ITFSServer, (LPVOID *)&pITfs);
    if (FAILED(hr))
    {
        SysFreeString(ConnectionString);
        return HRError(hr);
    }

    // Authenticate with the TFS server.
    hr = pITfs->Connect(ConnectionString, Username, Password);
    if (FAILED(hr) ||
        FAILED(hr = pITfs->get_AuthenticatedUser(&m_pObj->m_AuthenticatedUser)))
    {
        SysFreeString(ConnectionString);
        return HRError(hr);
    }
    m_pObj->m_ConnectionString = SysAllocString(ConnectionString);
    SysFreeString(ConnectionString);
    m_pObj->m_pITfs = pITfs;
    m_pObj->m_bLoggedIn = TRUE;
    return S_OK;
}

//*************************************************************************************************
// Get the srcsafe.ini path that we originally openned.  For TFS, this is just the connection
// string that was passed.
//*************************************************************************************************
HRESULT CImpIVSSDatabase::get_SrcSafeIni(	// Return status.
    BSTR		*pSrcSafeIni)				// Buffer for the srcsafe.ini spec.
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);

    Trace("Enter CImpIVSSDatabase::get_SrcSafeIni(this = \"%ls\")", m_pObj->m_ConnectionString);

    // Verify we are initialized.
    if (!m_pObj->m_bLoggedIn)
    {
        return SSError(ERS_SSAUTO_NOTOPEN);
    }

    *pSrcSafeIni = SysAllocString(m_pObj->m_ConnectionString);
    return S_OK;
}

//*************************************************************************************************
// Get the name of the database that we have open.
//*************************************************************************************************
HRESULT CImpIVSSDatabase::get_DatabaseName(	// Return status.
    BSTR		*pDatabaseName)				// Buffer for the database name.
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);
    VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSDatabase::get_DatabaseName(this = \"%ls\")", m_pObj->m_ConnectionString);

    //TODO: Parse the server name out of the URL.
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Get the name of the user we are logged in as..
//*************************************************************************************************
HRESULT CImpIVSSDatabase::get_UserName(		// Return status.
    BSTR		*pUsername)					// Buffer for the user name.
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);
    VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSDatabase::get_UserName(this = \"%ls\")", m_pObj->m_ConnectionString);

    // Verify we are initialized.
    if (!m_pObj->m_bLoggedIn)
    {
        return SSError(ERS_SSAUTO_NOTOPEN);
    }

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Get the spec of the current SourceSafe project.
//*************************************************************************************************
HRESULT CImpIVSSDatabase::get_CurrentProject(// Return status.
    BSTR		*pPrj)						// Buffer for the project.
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);
    VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSDatabase::get_CurrentProject(this = \"%ls\")", m_pObj->m_ConnectionString);

    // Verify we are initialized.
    if (!m_pObj->m_bLoggedIn)
    {
        return SSError(ERS_SSAUTO_NOTOPEN);
    }

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Set the current SourceSafe project.
//*************************************************************************************************
HRESULT CImpIVSSDatabase::put_CurrentProject(// Return status.
    BSTR		Prj)						// Spec of the project.
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);
    VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSDatabase::put_CurrentProject(this = \"%ls\", Prj = \"%ls\")", m_pObj->m_ConnectionString, Prj);

    // Verify we are initialized.
    if (!m_pObj->m_bLoggedIn)
    {
        return SSError(ERS_SSAUTO_NOTOPEN);
    }

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Get the VSSItem for the supplied SourceSafe spec.
//*************************************************************************************************
HRESULT CImpIVSSDatabase::get_VSSItem(		// Return status.
    BSTR		Spec,						// The SourceSafe spec.
    VARIANT_BOOL Deleted,					// Looking for deleted or not?
    IVSSItem	**ppIVSSItem)				// The retrieved item.
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);
    VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSDatabase::get_VSSItem(this = \"%ls\", Spec = \"%ls\", Deleted = %d)", m_pObj->m_ConnectionString, Spec, Deleted);

    // Verify we are initialized.
    if (!m_pObj->m_bLoggedIn)
    {
        return SSError(ERS_SSAUTO_NOTOPEN);
    }

    // Get the item from TFS.
    HRESULT hr;
    TFSItem sItem;
    if (!Deleted)
    {
        // If the item is not deleted, that's easy.
        hr = m_pObj->m_pITfs->GetItem(Spec, &sItem);
    }
    else
    {
        //TODO:
        return HRError(E_NOTIMPL);
    }
    if (FAILED(hr))
    {
        return HRError(hr);
    }

    // Wrap it in a VSSItem object.
    if (FAILED(hr = CVSSItem::NewVSSItem(sItem, m_pObj, ppIVSSItem)))
    {
        sItem.Delete();
        return HRError(hr);
    }
    return S_OK;
}

//*************************************************************************************************
// Add a user to the SourceSafe database.
//*************************************************************************************************
HRESULT CImpIVSSDatabase::AddUser(			// Return status.
    BSTR		Name,						// User's name.
    BSTR		Password,					// His password.
    VARIANT_BOOL ReadOnly,					// User is read-only?
    IVSSUser	**ppIUser)					// Returned user.
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);
    VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSDatabase::AddUser(this = \"%ls\", Name = \"%ls\", ReadOnly = \"%ls\")", m_pObj->m_ConnectionString, Name, ReadOnly);

    // Verify we are initialized.
    if (!m_pObj->m_bLoggedIn)
    {
        return SSError(ERS_SSAUTO_NOTOPEN);
    }

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Find a user in the database.
//*************************************************************************************************
HRESULT CImpIVSSDatabase::get_User(			// Return status.
    BSTR		Name,						// User's name.
    IVSSUser	**ppIUser)					// Returned user.
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);
    VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSDatabase::get_User(this = \"%ls\", Name = \"%ls\")", m_pObj->m_ConnectionString, Name);

    // Verify we are initialized.
    if (!m_pObj->m_bLoggedIn)
    {
        return SSError(ERS_SSAUTO_NOTOPEN);
    }

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Find a user in the database.
//*************************************************************************************************
HRESULT CImpIVSSDatabase::get_Users(		// Return status.
    IVSSUsers	**ppIUsers)					// Returned user collection.
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);
    VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSDatabase::get_Users(this = \"%ls\")", m_pObj->m_ConnectionString);

    // Verify we are initialized.
    if (!m_pObj->m_bLoggedIn)
    {
        return SSError(ERS_SSAUTO_NOTOPEN);
    }

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Determine if the project rights are enabled.
//*************************************************************************************************
HRESULT CImpIVSSDatabase::get_ProjectRightsEnabled(
    VARIANT_BOOL *pEnabled)
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);
    VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSDatabase::get_ProjectRightsEnabled(this = \"%ls\")", m_pObj->m_ConnectionString);

    // Verify we are initialized.
    if (!m_pObj->m_bLoggedIn)
    {
        return SSError(ERS_SSAUTO_NOTOPEN);
    }

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Enable/Disable the project rights.
//*************************************************************************************************
HRESULT CImpIVSSDatabase::put_ProjectRightsEnabled(
    VARIANT_BOOL Enabled)
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);
    VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSDatabase::put_ProjectRightsEnabled(this = \"%ls\", Enabled = %d)", m_pObj->m_ConnectionString, Enabled);

    // Verify we are initialized.
    if (!m_pObj->m_bLoggedIn)
    {
        return SSError(ERS_SSAUTO_NOTOPEN);
    }

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Get the default project rights.
//*************************************************************************************************
HRESULT CImpIVSSDatabase::get_DefaultProjectRights(
    long		*pRights)
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);
    VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSDatabase::get_DefaultProjectRights(this = \"%ls\")", m_pObj->m_ConnectionString);

    // Verify we are initialized.
    if (!m_pObj->m_bLoggedIn)
    {
        return SSError(ERS_SSAUTO_NOTOPEN);
    }

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Set the default project rights.
//*************************************************************************************************
HRESULT CImpIVSSDatabase::put_DefaultProjectRights(
    long		Rights)
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);
    VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSDatabase::put_DefaultProjectRights(this = \"%ls\", Rights = %d)", m_pObj->m_ConnectionString, Rights);

    // Verify we are initialized.
    if (!m_pObj->m_bLoggedIn)
    {
        return SSError(ERS_SSAUTO_NOTOPEN);
    }

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Get ini setting
//*************************************************************************************************
HRESULT CImpIVSSDatabase::GetSetting(
    BSTR		Setting,
    BSTR		*pValue)
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);
    VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSDatabase::GetSetting(this = \"%ls\", Setting = \"%ls\")", m_pObj->m_ConnectionString, UnNULLStr(Setting));

    // Verify we are initialized.
    if (!m_pObj->m_bLoggedIn)
    {
        return SSError(ERS_SSAUTO_NOTOPEN);
    }

    //TODO:
    return HRError(E_NOTIMPL);
}

//*************************************************************************************************
// Close the database
//*************************************************************************************************
HRESULT CImpIVSSDatabase::Close()
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);
    VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSDatabase::Close(this = \"%ls\")", m_pObj->m_ConnectionString);

    CAutoLock sLock(&gsMutex);
    HRESULT		hr = E_FAIL;

    if (FAILED(hr = m_pObj->Close()))
    {
        return HRError(hr);
    }

    return S_OK;
}

//*************************************************************************************************
// Get the impersonation property flag
//*************************************************************************************************
HRESULT CImpIVSSDatabase::get_ImpersonateCaller(
    VARIANT_BOOL *pImpersonate)
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);
    VERIFY_IMPERSONATION

    Trace("Enter CImpIVSSDatabase::get_ImpersonateCaller(this = \"%ls\")", m_pObj->m_ConnectionString);

    if (!pImpersonate)
    {
        return E_POINTER;
    }

    CAutoLock sLock(&gsMutex);

    *pImpersonate = gfImpersonate ? VARIANT_TRUE : VARIANT_FALSE;
    return S_OK;
}

//*************************************************************************************************
//*************************************************************************************************
HRESULT CImpIVSSDatabase::put_ImpersonateCaller(
    VARIANT_BOOL Impersonate)
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);
    gfImpersonate = (Impersonate == VARIANT_TRUE);

    Trace("Enter CImpIVSSDatabase::put_ImpersonateCaller(this = \"%ls\", Impersonate = %d)", m_pObj->m_ConnectionString, Impersonate);

    return S_OK;
}

//*************************************************************************************************
//*************************************************************************************************
#pragma warning(disable : 4355)			// Warning for "this" in initializer.
CVSSDatabase::CVSSDatabase() :
    m_cRef(0),
    m_ISupErrInfo(this),
    m_IVSSDB(this),
    m_bLoggedIn(0),
    m_ConnectionString(NULL),
    m_pIWorkspace(NULL)
#pragma warning(default : 4355)
{
    CAutoLock sLock(&gsMutex);

    InterlockedIncrement(&giObj);
}

//*************************************************************************************************
// Shutdown this instance of the SourceSafe database object.
//*************************************************************************************************
CVSSDatabase::~CVSSDatabase()
{
    Close();

    InterlockedDecrement(&giObj);
}

//*************************************************************************************************
// Check if we support the requested interface and set the interface pointer appropriately.
//*************************************************************************************************
HRESULT CVSSDatabase::QueryInterface(		// Did we get an interface?
    REFIID		riid,						// ID of the interface we want.
    LPVOID		*ppvObj)					// Buffer for the interface pointer.
{
    // Init the returned interface pointer to NULL.
    *ppvObj = NULL;

    Trace("Enter CVSSDatabase::QueryInterface");

    // If we support the requested interface pointer, save it.
    if (riid == IID_IUnknown)
    {
        *ppvObj = this;
    }
    else if (riid == IID_IDispatch || 
             riid == IID_IVSSDatabase ||
             riid == IID_IVSSDatabaseOld ||
             riid == IID_IVSSDatabaseOld1 )
    {
        *ppvObj = &m_IVSSDB;
    }
    else if (riid == IID_ISupportErrorInfo)
    {
        *ppvObj = &m_ISupErrInfo;
    }
    else
    {
        return E_NOINTERFACE;
    }

    // AddRef the interface before we return it.
    ((LPUNKNOWN) *ppvObj)->AddRef();
    return S_OK;
}

//*************************************************************************************************
// Close down the current database connection
//*************************************************************************************************
HRESULT CVSSDatabase::Close()
{
    INITPUBLICMETHOD(&IID_IVSSDatabase);

    if (m_ConnectionString != NULL)
    {
        SysFreeString(m_ConnectionString);
        m_ConnectionString = NULL;
    }
    if (m_pIWorkspace != NULL)
    {
        m_pIWorkspace->Release();
        m_pIWorkspace = NULL;
    }
    if (m_pITfs != NULL)
    {
        m_pITfs->Release();
        m_pITfs = NULL;
    }
    m_bLoggedIn = FALSE;
    return S_OK;
}

//*************************************************************************************************
// Get a reference to the TFS workspace the FrontPage server extensions uses.  This does not AddRef
// the interface pointer, so the caller must not release it.
//*************************************************************************************************
HRESULT CVSSDatabase::GetFPSEWorkspace(
    ITFSWorkspace **ppIWorkspace)
{
    if (m_pIWorkspace == NULL)
    {
        BSTR WorkspaceName = SysAllocString(L"FPSE_Workspace");
        BSTR Username = SysAllocString(L".");
        HRESULT hr = m_pITfs->GetWorkspace(WorkspaceName, Username, &m_pIWorkspace);
        //TODO: Check that this is the right error.
        if (hr == 0x80131600)
        {
            BSTR Comment = SysAllocString(L"A workspace used by FrontPage server extensions to version files using TFS.");
            hr = m_pITfs->CreateWorkspace(WorkspaceName, Username, Comment, &m_pIWorkspace);
            SysFreeString(Comment);
        }
        if (FAILED(hr))
        {
            SysFreeString(WorkspaceName);
            SysFreeString(Username);
            return HRError(hr);
        }
        SysFreeString(WorkspaceName);
        SysFreeString(Username);
    }
    *ppIWorkspace = m_pIWorkspace;
    return S_OK;
}

//*************************************************************************************************
// Convert a SourceSafe error into an HRESULT.
//*************************************************************************************************
HRESULT SSError(							// Return status.
    int			iStat,						// The SourceSafe error.
    ...)									// Error arguments.
{
    if (iStat >= 0)
    {
        return S_OK;
    }

    Trace("Error");

    //TODO: Load the resource and fill the error info.
    FillErrorInfo(L"An error occurred.", -iStat);

    // Return the error as an HResult.
    return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, (USHORT)iStat);
}

//*************************************************************************************************
// This handles errors that are generated as HRESULT errors.  It converts to an ERS_ error number
// to report the error, then returns the original HRESULT.
//*************************************************************************************************
HRESULT HRError(							// Return status.
    HRESULT		hr)							// HRESULT error.
{
    // Ignore if it wasn't really an error.
    if (SUCCEEDED(hr))
    {
        return hr;
    }

    // Find an appropriate SS error.
    switch (hr)
    {
        case E_POINTER:
        case DISP_E_UNKNOWNLCID:
        case DISP_E_OVERFLOW:
        case DISP_E_PARAMNOTFOUND:
        case E_INVALIDARG:
        case CLASS_E_NOAGGREGATION:
        case E_NOINTERFACE:
        case DISP_E_UNKNOWNINTERFACE:
        case (__HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION)):
        {
            SSError(ERS_FILE_BADPARAM);
            break;
        }
        case E_OUTOFMEMORY:
        {
            SSError(ERS_MEM_NOMEMORY);
            break;
        }
        case E_NOTIMPL:
        {
            SSError(ERS_NOTIMPL);
            break;
        }
        case TYPE_E_ELEMENTNOTFOUND:
        case REGDB_E_WRITEREGDB:
        case CONNECT_E_NOCONNECTION:
        case CONNECT_E_ADVISELIMIT:
        case CONNECT_E_CANNOTCONNECT:
        {
            SSError(ERS_UNKNOWN);
            break;
        }
    }

    Trace("Error");

    // Return the HRESULT.
    return hr;
}

int SSAutoEnter::m_iEntered = 0;

//*************************************************************************************************
// This constructor is executed on entry into every public method in this API.
//*************************************************************************************************
SSAutoEnter::SSAutoEnter(
    const IID	*psIID)	:					// Interface we are in.
    m_psIID(psIID)
{
    if (++m_iEntered == 1)
    {
        // Clear any existing error.
        SetErrorInfo(0L, NULL);
    }
}

//*************************************************************************************************
// This is executed on exit from every public method in this API.
//*************************************************************************************************
SSAutoEnter::~SSAutoEnter()
{
    IErrorInfo	*pIErrInfo;					// Error info object.
    ICreateErrorInfo *pICreateErr;			// Error info creation Iface pointer.

    if (--m_iEntered == 0)
    {
        // If there was an error, set the interface ID.
        if (GetErrorInfo(0L, &pIErrInfo) == S_OK)
        {
            if (pIErrInfo->QueryInterface(IID_ICreateErrorInfo,	(PVOID *) &pICreateErr) == S_OK)
            {
                pICreateErr->SetGUID(*m_psIID);
                pICreateErr->Release();
            }
            SetErrorInfo(0L, pIErrInfo);
            pIErrInfo->Release();
        }
    }
}

//*************************************************************************************************
// Create, fill out and set an error info object.  Note that this does not fill out the IID for
// the error object; that is done elsewhere.
//*************************************************************************************************
HRESULT FillErrorInfo(						// Return status.
    LPCWSTR		szMsg,						// Error message.
    DWORD		dwHelpContext)				// Help context.
{
    ICreateErrorInfo *pICreateErr;			// Error info creation Iface pointer.
    IErrorInfo	*pIErrInfo;					// The IErrorInfo interface.
    HRESULT		hr;							// Return status.

    // Get the ICreateErrorInfo pointer.
    if (FAILED(hr = CreateErrorInfo(&pICreateErr)))
    {
        return hr;
    }

    // Set the progid of the class.
    if (FAILED(hr = pICreateErr->SetSource(L"SourceSafe")))
    {
        pICreateErr->Release();
        return hr;
    }

    // Set the error description.
    if (FAILED(hr = pICreateErr->SetDescription((LPOLESTR)szMsg)))
    {
        pICreateErr->Release();
        return hr;
    }

    // Get the IErrorInfo pointer.
    if (FAILED(hr = pICreateErr->QueryInterface(IID_IErrorInfo, (PVOID *) &pIErrInfo)))
    {
        pICreateErr->Release();
        return hr;
    }

    // Save the error and release our local pointers.
    SetErrorInfo(0L, pIErrInfo);
    pIErrInfo->Release();
    pICreateErr->Release();
    return S_OK;
}
